package com.nhjf.dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.json.JSONArray;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.LockOptions;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.internal.CriteriaImpl.Subcriteria;
import org.hibernate.transform.ResultTransformer;

import com.nhjf.common.hibernate.HibernateBaseDao;
import com.nhjf.common.hibernate.MyResultTransformer;
import com.nhjf.common.page.Pagination;
import com.nhjf.common.util.MyBeanUtils;
import com.nhjf.model.BaseModel;
import com.nhjf.pojo.qo.BaseQo;

/**
 * Hibernate基础查询DAO
 * @author zhurz
 *
 * @param <T>
 * @param <QO>
 */
public abstract class BaseDao<T extends BaseModel, QO extends BaseQo> extends HibernateBaseDao<T, Serializable> {

	/**
	 * 查询唯一对象
	 * 
	 * @param qo
	 * @return
	 */
	public T queryUnique(QO qo) {
		List<T> list = queryList(qo, 1);
		if (list != null && list.size() > 0) {
			return list.get(0);
		}
		return null;
	}

	/**
	 * 查询对象列表
	 * 
	 * @param qo
	 * @return
	 */
	public List<T> queryList(QO qo) {
		return queryList(qo, 0, null);
	}

	/**
	 * 查询实体列表
	 * 
	 * @param qo
	 * @param maxSize		最大获取数量
	 * @return
	 */
	public List<T> queryList(QO qo, Integer maxSize){
		return queryList(qo, 0, maxSize);
	}
	
	/**
	 * 查询实体列表
	 * 
	 * @param qo
	 * @param offset		偏移量
	 * @param maxSize		最大获取数量
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> queryList(QO qo, Integer offset, Integer maxSize) {
		Criteria criteria = createCriteria(qo);
		if (offset != null && offset >= 0) {
			criteria.setFirstResult(offset);
		}
		if (maxSize != null && maxSize >= 1) {
			criteria.setMaxResults(maxSize);
		}
		return criteria.list();
	}
	
	/**
	 * 查询数量
	 * 
	 * @param qo
	 * @return
	 */
	public Integer queryCount(QO qo) {
		Criteria criteria = createCriteria(qo);
		CriteriaImpl impl = (CriteriaImpl) criteria;
		MyBeanUtils.setFieldValue(impl, ORDER_ENTRIES, new ArrayList<Object>());
		int totalCount = ((Number) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
		return totalCount;
	}

	/**
	 * 分页查询对象
	 * 
	 * @param pagination
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Pagination queryPagination(Pagination pagination) {
		if (pagination.getCondition() == null || !(pagination.getCondition() instanceof BaseQo)) {
			pagination.setCondition(new BaseQo());
		}
		Criteria criteria = createCriteria((QO) pagination.getCondition());
		return super.findByCriteria(criteria, pagination);
	}
	
	private Criteria buildBaseCriteria(Criteria criteria, QO qo) {
		if (qo != null) {
			if (qo.getId() != null) {
				criteria.add(Restrictions.eq("id", qo.getId()));
			}
			if (qo.getIdNotIn() != null && qo.getIdNotIn().length > 0) {
				criteria.add(Restrictions.and(eachNeProperty(qo.getIdNotIn(), "id")));
			}
		}
		return criteria;
	}

	/**
	 * 创建Hibernate标准查询
	 * 
	 * @param qo
	 * @return
	 */
	protected Criteria createCriteria(QO qo){
		Criteria criteria = super.getSession().createCriteria(getEntityClass());
		buildIfProjectionResultCriteria(criteria, qo);
		buildBaseCriteria(criteria, qo);
		buildCriteria(criteria, qo);
		return criteria;
	}
	
	public Criteria buildCriteriaOut(Criteria criteria, QO qo) {
		buildCriteria(criteria, qo);
		buildBaseCriteria(criteria, qo);
		buildIfProjectionResultCriteria(criteria, qo);
		return criteria;
	}
	
	public T executeHQL(String hql,Object... values){
//		Query query = super.getSession().createSQLQuery(hql);
//		List<T> list = query.list();
//		if (list != null && list.size() > 0) {
//			return list.get(0);
//		}
//		return null;
		return (T) super.findUnique(hql, values);
	}

	/**
	 * 为Hibernate标准查询添加条件
	 * 
	 * @param criteria
	 * @return
	 */
	abstract protected Criteria buildCriteria(Criteria criteria, QO qo);
	
	/**
	 * 执行HQL语句
	 * 
	 * @param hql
	 * @param params
	 * @return
	 */
	public int executeHql(String hql, Object... params) {
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < params.length; i++) {
			query.setParameter(i, params[i]);
		}
		return query.executeUpdate();
	}
	
	public int executeSql(String sql,Object... params){
		Query query = getSession().createSQLQuery(sql);
		for (int i = 0; i < params.length; i++) {
			query.setParameter(i, params[i]);
		}
		return query.executeUpdate();
	}
	
	public void save(T entity) {
		super.save(entity);
	}

	public void saveOrUpdate(T entity) {
		super.saveOrUpdate(entity);
	}

	@SuppressWarnings("unchecked")
	public T merge(T entity) {
		return (T) super.getSession().merge(entity);
	}

	public void update(T entity) {
		super.update(entity);
	}

	public void delete(T entity) {
		super.delete(entity);
	}

	public void deleteById(Serializable id) {
		T entity = get(id);
		if(entity != null){
			super.delete(get(id));
		}
	}

	public T get(Serializable id) {
		return super.get(id);
	}
	
	public T load(Serializable id) {
		return super.load(id);
	}

	public void lockUpgrade(T entity) {
		super.getSession().buildLockRequest(LockOptions.UPGRADE).lock(entity);
	}

	public void evict(T entity) {
		super.evict(entity);
	}

	public void saveArray(T... entitys) {
		for (T entity : entitys) {
			save(entity);
		}
	}

	public void saveList(List<T> list) {
		for (T entity : list) {
			save(entity);
		}
	}

	public void updateArray(T... entitys) {
		for (T entity : entitys) {
			update(entity);
		}
	}

	public void updateList(List<T> list) {
		for (T entity : list) {
			update(entity);
		}
	}

	public void deleteByIds(Serializable... ids) {
		for(Serializable id:ids){
			deleteById(id);
		}
	}

	public void flush() {
		super.getSession().flush();
	}

	public void clear() {
		super.getSession().clear();
	}

	public Double sumProperty(String propertyName, QO qo) {
		Criteria criteria = createCriteria(qo);
		CriteriaImpl impl = (CriteriaImpl) criteria;
		MyBeanUtils.setFieldValue(impl, ORDER_ENTRIES, new ArrayList<Object>());
		Number number = ((Number) criteria.setProjection(Projections.sum(propertyName)).uniqueResult());
		return number == null ? 0d : number.doubleValue();
	}

	public Double avgProperty(String propertyName, QO qo) {
		Criteria criteria = createCriteria(qo);
		CriteriaImpl impl = (CriteriaImpl) criteria;
		MyBeanUtils.setFieldValue(impl, ORDER_ENTRIES, new ArrayList<Object>());
		Number number = ((Number) criteria.setProjection(Projections.avg(propertyName)).uniqueResult());
		return number == null ? 0d : number.doubleValue();
	}
	
	/**
	 * 遍历数组条件（代替IN查询）
	 * 
	 * @param objs
	 * @param propertyName
	 * @return
	 */
	protected Criterion[] eachEqProperty(Object[] objs, String propertyName) {
		Criterion[] criterions = new Criterion[objs.length];
		for (int i = 0; i < objs.length; i++) {
			criterions[i] = Restrictions.eq(propertyName, objs[i]);
		}
		return criterions;
	}
	
	/**
	 * 遍历数组条件（代替NOT IN查询）
	 * 
	 * @param objs
	 * @param propertyName
	 * @return
	 */
	protected Criterion[] eachNeProperty(Object[] objs, String propertyName) {
		Criterion[] criterions = new Criterion[objs.length];
		for (int i = 0; i < objs.length; i++) {
			criterions[i] = Restrictions.ne(propertyName, objs[i]);
		}
		return criterions;
	}

	/**
	 * sql 查询
	 * 
	 * @param sql
	 * @param propertyName
	 * @return
	 */
	public List queryListForSql( String sql ,Object[] parms) {
		Session  session = this.sessionFactory.openSession() ;
		 Query query = session.createSQLQuery(sql);
		 if(null != parms && parms.length>0){
			 for (int i = 0; i < parms.length; i++) {
				 query.setParameter(i, parms[i]);
			 }
		 }
		List list = query.list();
		session.close();
		return null==list||list.size()==0?null:list;
	}
	
	/**
	 * sql 查询
	 * 
	 * @param queryListForSql to List < map > 
	 * @param propertyName
	 * @return
	 */
	public List< Map<String, Object>> BaseFormatToMap( List date ,String [] orderByName) {
		 List< Map<String, Object>>  listMap = new ArrayList<Map<String,Object>>();
		 
		 if(null==date|| date.size()==0){return null; }
		 
		 for (int i = 0; i < date.size(); i++) {
	 
					JSONArray json = new JSONArray().fromObject(date.get(i));
					Map<String, Object> map = new HashMap<String, Object>();
						for (int j = 0; j < json.size(); j++) {
							map.put(orderByName[j], json.get(j));
						}
					listMap.add(map);
				 
		 }
		 return listMap;
	}
	
	/**
	 * sql 查询
	 * 
	 * @param queryListForSql to map 
	 * @param propertyName
	 * @return
	 */
	public Map<String, Object> BaseFormatToObject( List date ,String [] orderByName) {
		 Map<String, Object>  Map = new HashMap() ;
		 
		 if(null==date|| date.size()==0){return null; }
		 
		 for (int i = 0; i < date.size(); i++) {
					JSONArray json = new JSONArray().fromObject(date.get(i));
					for (int j = 0; j < json.size(); j++) {
						Map.put(orderByName[j], json.get(j));
					}
		 }
		 return Map;
	}
	/**
	 * 构建如果是指定字段的查询且必须指定别名
	 * 
	 * @param criteria
	 * @param qo
	 */
	protected void buildIfProjectionResultCriteria(Criteria criteria, QO qo) {
		if (qo.getProjectionPropertys() != null && qo.getProjectionPropertys().length > 0) {
			if (criteria instanceof CriteriaImpl) {
				ProjectionList projectionList = Projections.projectionList();
				for (String property : qo.getProjectionPropertys()) {
					projectionList.add(Projections.property(property), property);
				}
				criteria.setProjection(projectionList);
				criteria.setResultTransformer(new MyResultTransformer(getEntityClass()));
			} else if (criteria instanceof Subcriteria && qo.getAlias() != null) {
				StringBuilder aliasSb = new StringBuilder("");
				StringBuilder propertySb = new StringBuilder("");
				Criteria preCriteria = criteria;
				while (preCriteria instanceof Subcriteria) {
					Subcriteria currentSubcriteria = (Subcriteria) preCriteria;
					aliasSb.insert(0, currentSubcriteria.getAlias());
					propertySb.insert(0, currentSubcriteria.getPath());
					preCriteria = currentSubcriteria.getParent();
					if (preCriteria instanceof Subcriteria) {
						aliasSb.insert(0, ".");
						propertySb.insert(0, ".");
					}
				}
				String alias = aliasSb.toString();
				String propertyPath = propertySb.toString();
				ProjectionList projectionList = Projections.projectionList();
				if (preCriteria instanceof CriteriaImpl) {
					CriteriaImpl criteriaImpl = (CriteriaImpl) preCriteria;
					if (criteriaImpl.getProjection() != null) {
						projectionList = (ProjectionList) criteriaImpl.getProjection();
					}
					for (String property : qo.getProjectionPropertys()) {
						projectionList.add(Projections.property(alias + "." + property), propertyPath + "." + property);
					}
					ResultTransformer oldRtf = criteriaImpl.getResultTransformer();
					criteriaImpl.setProjection(projectionList);
					if (oldRtf != null) {
						criteriaImpl.setResultTransformer(oldRtf);
					}
				}
			}
		}
	}
}
